/*-------------------------------------------------------------------------
 - Copyright (c) 2024-2025 [XD-AMCC TEAM]
 - [XD-AMCC] is licensed under Mulan PSL v2.
 - You can use this software according to the terms and conditions of the Mulan PSL v2.
 - You may obtain a copy of Mulan PSL v2 at:
 -          http://license.coscl.org.cn/MulanPSL2
 - THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES
 - OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 - TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 - See the Mulan PSL v2 for more details.
 ------------------------------------------------------------------------*/

/**
 * @brief  Semantic actions for translating declarations.
 *
 * @author  WXQ#XDU
 * @date    2025.01
 */


#include "_emit_inner.h"


// pre-semantic-action for translationUnit:
//   Prepare to translate the source program,
//   PUSH a new func-info onto the nesting path stack.
//
void emit_enter_translationUnit(t_tree_listener_ptr listenerPtr, t_tree_ptr node)
{
    t_emitter_ptr  pEmitter = (t_emitter_ptr)listenerPtr;
    pEmitter->nErrors = error_get_count();
    emitter_entry_func(pEmitter, node);

    log_trace("Translating the translation-unit ...");
}


// post-semantic-action for translationUnit:
//   POP func-info from the nesting path stack,
//   End translation of the source program
//
void emit_exit_translationUnit(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_emitter_ptr  pEmitter = (t_emitter_ptr)listenerPtr;
    emitter_exit_func(pEmitter);
    pEmitter->nErrors = error_get_count() - pEmitter->nErrors;

    log_trace("Translated  the translation-unit");
}


// pre-semantic-action for functionDeclaration:
//   Prepare to translate a function,
//   PUSH a new func-info onto the nesting path stack.
//
void emit_enter_functionDeclaration(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_emitter_ptr  pEmitter = (t_emitter_ptr)listenerPtr;

    if (LOG_TRACE_ENABLE()) {
        t_tree_ptr funcNameNode = tree_get_child(node, 1);
        t_token_ptr    pIdToken = tree_get_token(funcNameNode);
        t_position_ptr      pos = token_get_position(pIdToken);
        log_trace("%.*sTranslating function %s at %d:%d ...",
            2 * stack_size(&pEmitter->functionStack),
            amcc_long_spacestring(),
            token_get_text(pIdToken), pos->line, pos->column);
    }

    emitter_entry_func(pEmitter, node);
}


// post-semantic-action for functionDeclaration:
//  POP func-info from the nesting path stack,
//  End translation of a function
//
void emit_exit_functionDeclaration(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_emitter_ptr  pEmitter = (t_emitter_ptr)listenerPtr;
    emitter_exit_func(pEmitter);

    if (LOG_TRACE_ENABLE()) {
        t_tree_ptr funcNameNode = tree_get_child(node, 1);
        t_token_ptr    pIdToken = tree_get_token(funcNameNode);
        t_position_ptr      pos = token_get_position(pIdToken);
        log_trace("%.*sTranslated  function %s at %d:%d",
            2 * stack_size(&pEmitter->functionStack),
            amcc_long_spacestring(),
            token_get_text(pIdToken), pos->line, pos->column);
    }
}


// post-semantic-action for parameterList:
//   Emit codes simulating / indicating argument-passing of RumTime-Environment,
//      arg_name_K = arg K
//
void emit_exit_parameterList(t_tree_listener_ptr listenerPtr, t_tree_ptr node) {
    t_emitter_ptr  pEmitter = (t_emitter_ptr) listenerPtr;
    t_tree_ptr   childNode0 = tree_get_child(node, 0);
    if ( tree_is_epsilon(childNode0) ) {  // no parameter
        return;
    }

    t_list_iterator  itr;   // prepare to visit parameter 1..N
    LIST_ITERATOR_INIT ( &itr, tree_get_children(node) );
    t_list_node_ptr subNode = LIST_ITERATOR_BEGIN ( &itr );
    t_list_node_ptr     end = LIST_ITERATOR_END   ( &itr );
    for (t_ircode_id n=0 ; subNode != end ; subNode = LIST_ITERATOR_NEXT(&itr) ) {
        t_tree_ptr      paramNode = (t_tree_ptr)list_node_get_value(subNode);
        t_tree_ptr directDeclNode = tree_get_child(paramNode, 1);
        t_tree_ptr  paramNameNode = tree_get_child(directDeclNode, 0);

        // NOTICE:  for emitting code with the sequence number n for parameter,
        //          this loop-body can not perform during analyzing a parameter,
        //          as visit-function emit_exit_parameter().
        t_ir_address_ptr
        numberAddr    = codemgr_newNumber (pEmitter->ircodemgrPtr, ++n);
        t_ir_address_ptr
        paramNameAddr = codemgr_newToken(pEmitter->ircodemgrPtr, paramNameNode);
        t_ircode_ptr
        codePtr       = codemgr_emit(pEmitter->ircodemgrPtr,   OP_ARG,
                            numberAddr, NULL, paramNameAddr);
        codePtr->comment.text = CODEDESC_PASSARG;
        subNode = LIST_ITERATOR_NEXT(&itr); // jump ","
    }
}



struct  wrap_initor {
    t_emitter_ptr  pEmitter;
    t_tree_ptr     varNameNode;
    t_type_ptr     arrayType;
    size_t         elementWidth;
};


// Emit codes initializing one array element which have corresponding initializer.
//
static int  emit_array_initor__(t_list_app_value app_data,
                                void * user_data)
{
    struct  wrap_initor * wrapper = (struct  wrap_initor*) user_data;
    t_init_expr_ptr initExpr = (t_init_expr_ptr) app_data;
    t_codeattr_ptr  exprAttr = (t_codeattr_ptr) tree_get_annotation(initExpr->exprNode);

    // compute the offset of the current element, in Compiling-Time,
    // according to the formula (4.6) in section 4.7.1 of our textbook
    //  v[1] = i[1],  v[k] = v[k-1]*d[k] + i[k],
    //  offset = v[n] * w

    int     dimensions = initExpr->indexHolders[0];
    size_t  offset     = initExpr->indexHolders[1];                // v[1]
    for ( int dimK=2; dimK <= dimensions; ++dimK ) {
        int dimLen = type_var_dimlength(wrapper->arrayType, dimK); // the d[k]
        offset = offset * dimLen + initExpr->indexHolders[dimK];   //v[k] = v[k-1] * d[k] + i[k]
    }
    offset = offset * wrapper->elementWidth;   // v[n] * w

    // In section 4.7.2 of textbook, for production V -> EL ], emit code like
    //       V.place = EL.array - C
    // BUT for AMC,  this C is always 0.

    // memory address of array, V.place
    t_ir_address_ptr V_place  = codemgr_newToken(
                            wrapper->pEmitter->ircodemgrPtr,
                            wrapper->varNameNode);
    // memory offset of element, V.offset
    t_ir_address_ptr V_offset = codemgr_newNumber(
                            wrapper->pEmitter->ircodemgrPtr,  offset);
    // emit: V.place[V.offset] =  expr
    t_ircode_ptr codePtr = codemgr_emit( wrapper->pEmitter->ircodemgrPtr,
                            OP_ARR_WRITE, V_place, V_offset,
                            exprAttr->place);
    codePtr->comment.text = CODEDESC_INITVAR;
    return 0;
}


// Emit a code that fill 0 in the memory block of a variable
static void emit_variable_set0__(t_emitter_ptr  pEmitter,
                                 t_tree_ptr     varNameNode,
                                 size_t         varWidth )
{
    t_ir_address_ptr widthAddr = codemgr_newNumber(pEmitter->ircodemgrPtr,  varWidth);
    t_ir_address_ptr varAddr   = codemgr_newToken (pEmitter->ircodemgrPtr,  varNameNode);
    t_ircode_ptr codePtr = codemgr_emit(pEmitter->ircodemgrPtr,
                            OP_SET0, widthAddr, NULL, varAddr);
    codePtr->comment.text = CODEDESC_SETVAR0;
}


// post-semantic-action for initDeclarator:
//     emit code to initialize variables according to initializers.
//
// Here is the high level production:
//    initDeclarator -> directDeclarator ( '=' initializer )?
//    directDeclarator -> varName ( '[' N ']' )*
//
void emit_exit_initDeclarator(t_tree_listener_ptr listenerPtr, t_tree_ptr node)
{
    t_emitter_ptr  pEmitter = (t_emitter_ptr) listenerPtr;
    t_tree_ptr  varNameNode = tree_get_child(tree_get_child(node, 0), 0);
    t_symentry_ptr symEntry = tree_get_symbolEntry(varNameNode);
    t_tree_ptr     initNode = symentry_var_initializer(symEntry);
    t_type_ptr     varType  = symentry_type(symEntry);
    size_t         varWidth = type_width( varType );

// CASE 1: this initDeclarator has not an initializer, what to do?
    if (tree_get_childCount(node) != 3) {
        // Tips: the bottom in stack is for global scope
        int isGlobal = stack_size( &(pEmitter->functionStack) ) < 2;
        if ( isGlobal ) {
            // Set global variables to zero,
            // this is a pseudo operation simulating the Run-Time-Environment
            emit_variable_set0__( pEmitter, varNameNode, varWidth );
        }

        return;
    }

// CASE 2:  For a scalar x, emit:  var = value.place

    if ( type_is_scalar( varType ) ) {
        t_tree_ptr     exprNode  = tree_get_child(initNode, 0);
        t_ir_address_ptr valueAddr;   {
            t_codeattr_ptr valueAttr = (t_codeattr_ptr)tree_get_annotation(exprNode);
            if ( NULL == valueAttr ) {
                ERROR_REPORT_BUG("init-expr-attr for a scalar is NULL in %s", __FUNCTION__);
                return;
            }
            valueAddr = emit_getExprAddress__( pEmitter->ircodemgrPtr, valueAttr );
        }
        t_ir_address_ptr varAddr = codemgr_newToken( pEmitter->ircodemgrPtr, varNameNode );
        t_ircode_ptr     codePtr = codemgr_emit( pEmitter->ircodemgrPtr,  OP_ASSIGN,
                                    valueAddr, NULL, varAddr);
        codePtr->comment.text = CODEDESC_INITVAR;
        return;
    }

// CASE 3:  For one array, emit initializing code ONLY for elements
//          with corresponding initializers

    // (1) Set all elements to zero,
    // this is a pseudo operation simulating the Run-Time-Environment
    emit_variable_set0__( pEmitter, varNameNode, varWidth );

    // (2) get initializer expressions that are computed by symbol-table-builder
    t_list_ptr   initExprList = symentry_array_initExprs(symEntry);
    if (NULL == initExprList) {
        ERROR_REPORT_BUG("null init-value-expr list"
                         " for an array with initializer in %s",
                         __FUNCTION__ );
        return;
    }

    // (3) emit initializing code
    struct  wrap_initor wrapper;   {
        wrapper.pEmitter     = pEmitter;
        wrapper.varNameNode  = varNameNode;
        wrapper.arrayType    = varType;
        wrapper.elementWidth = type_width( type_var_base(varType) );
    }
    list_foreach(initExprList, emit_array_initor__,  &wrapper);
}
